﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Actor.Net.Location;
using Actor.Net.Message;
using Actor.Net.Network.Actor;

namespace Actor.Net
{
    /// <summary>
    /// Actor位置服务服务端，Actor服务注册、Actor服务发现、Actor生命周期管理。    
    /// </summary>
    public sealed class ActorLocationServer
    {
        //Group关系映射缓存
        private ConcurrentDictionary<string, ConcurrentDictionary<int, GroupInfo>> ServerGroupInfoMapping { get; } = new ConcurrentDictionary<string, ConcurrentDictionary<int, GroupInfo>>();
        private ConcurrentDictionary<int, ConcurrentDictionary<string, GroupInfo>> ServerGroupInfoChannelMapping { get; } = new ConcurrentDictionary<int, ConcurrentDictionary<string, GroupInfo>>();

        //Actor关系映射缓存
        private ConcurrentDictionary<string, ConcurrentDictionary<string, ActorLocationInfo>> ActorInfoServerGroupMapping { get; } = new ConcurrentDictionary<string, ConcurrentDictionary<string, ActorLocationInfo>>();
        private ConcurrentDictionary<string, ConcurrentDictionary<string, ActorLocationInfo>> ActorInfoTypeMapping { get; } = new ConcurrentDictionary<string, ConcurrentDictionary<string, ActorLocationInfo>>();
        private ConcurrentDictionary<string, ConcurrentDictionary<string, ActorLocationInfo>> ActorInfoHostMapping { get; } = new ConcurrentDictionary<string, ConcurrentDictionary<string, ActorLocationInfo>>();
        private ConcurrentDictionary<int, ConcurrentDictionary<string, ActorLocationInfo>> ActorInfoChannelMapping { get; } = new ConcurrentDictionary<int, ConcurrentDictionary<string, ActorLocationInfo>>();
        private ConcurrentDictionary<string, ActorLocationInfo> ActorInfoMapping { get; } = new ConcurrentDictionary<string, ActorLocationInfo>();

        private ConcurrentDictionary<string, ActorChannelContext> ChannelContextHostMapping { get; } = new ConcurrentDictionary<string, ActorChannelContext>();
        private ConcurrentDictionary<int, string> ChannelContextIHostKeyChannelIdMapping { get; } = new ConcurrentDictionary<int, string>();

        private ActorNetwork ServerNetwork { get; set; }

        /// <summary>
        /// Actor位置服务服务端实例
        /// </summary>
        public static ActorLocationServer Instance { get; } = new ActorLocationServer();

        /// <summary>
        /// 位置服务监听端口号
        /// </summary>
        public static int ListenPort = 48798;

        /// <summary>
        /// 构造函数
        /// </summary>
        private ActorLocationServer() { }

        /// <summary>
        /// 未捕获异常处理者
        /// </summary>
        public Action<ActorChannelContext, Exception> ExceptionHandler { get; set; }

        /// <summary>
        /// 启动Actor位置服务服务端
        /// </summary>
        /// <param name="blockCurrentThread">是否阻塞当前线程</param>
        public void Start(bool blockCurrentThread)
        {
            this.ServerNetwork = new ActorNetwork(ListenPort);
            this.ServerNetwork.Bind(this.GetType().Assembly);
            this.ServerNetwork.IsIsDropTimeout = false;

            this.ServerNetwork.AddDisconnectedHandler((context) =>
            {
                RemoveGroupInfo(context);
                if (ChannelContextIHostKeyChannelIdMapping.TryRemove(context.ChannelId, out string hostKey))
                {
                    ChannelContextHostMapping.TryRemove(hostKey, out ActorChannelContext value);
                }
            });
            ServerNetwork.IsIsDropTimeout = false;
            ServerNetwork.Listen();
            ActorNetwork.Start(blockCurrentThread);
        }

        /// <summary>
        /// 添加一个Actor分组信息
        /// </summary>
        /// <param name="context"></param>
        /// <param name="groupInfo"></param>
        public void AddGroupInfo(ActorTransferContext context, GroupInfo groupInfo)
        {
            groupInfo.ChannelId = context.ChannelId;
            if (!ServerGroupInfoChannelMapping.TryGetValue(groupInfo.ChannelId, out ConcurrentDictionary<string, GroupInfo> channelGroupInfos))
            {
                channelGroupInfos = new ConcurrentDictionary<string, GroupInfo>();
                ServerGroupInfoChannelMapping.AddOrUpdate(groupInfo.ChannelId, channelGroupInfos, (k, v) => channelGroupInfos);
            }
            channelGroupInfos.AddOrUpdate(groupInfo.ServerGroup, groupInfo, (k, v) => groupInfo);

            if (!ServerGroupInfoMapping.TryGetValue(groupInfo.ServerGroup, out ConcurrentDictionary<int, GroupInfo> groupChannelInfos))
            {
                groupChannelInfos = new ConcurrentDictionary<int, GroupInfo>();
                ServerGroupInfoMapping.AddOrUpdate(groupInfo.ServerGroup, groupChannelInfos, (k, v) => groupChannelInfos);
            }
            groupChannelInfos.AddOrUpdate(groupInfo.ChannelId, groupInfo, (k, v) => groupInfo);

            var hostKey = KeyGenerator.CreateHostKey(groupInfo.IpAddress, groupInfo.Port);
            ChannelContextHostMapping.AddOrUpdate(hostKey, context.ChannelContext, (k, v) => context.ChannelContext);
            ChannelContextIHostKeyChannelIdMapping.AddOrUpdate(context.ChannelId, hostKey, (k, v) => hostKey);
        }

        /// <summary>
        /// 获取Actor服务分组信息列表
        /// </summary>
        /// <param name="serverGroup"></param>
        /// <returns></returns>
        public List<GroupInfo> GetGroupInfoList(string serverGroup)
        {
            if (!ServerGroupInfoMapping.TryGetValue(serverGroup, out ConcurrentDictionary<int, GroupInfo> groupChannelInfos))
                return null;

            var datas = new List<GroupInfo>();
            foreach (var groupInfo in groupChannelInfos.Values)
            {
                if (datas.Where(g => g.ServerGroup == groupInfo.ServerGroup && g.IpAddress == groupInfo.IpAddress && g.Port == groupInfo.Port).Any())
                    continue;

                datas.Add(groupInfo);
            }
            return datas;
        }

        private void BroadcastRemoveGroupInfo(ActorChannelContext context, GroupInfo groupInfo)
        {
            var channelContexts = ServerNetwork.GetContextList();
            if (!channelContexts.Any())
                return;

            foreach (var channelContext in channelContexts)
            {
                try
                {
                    if (channelContext.ChannelId == context.ChannelId)
                        continue;
                }
                catch (Exception ex)
                {
                    ExceptionHandler?.Invoke(channelContext, ex);
                    continue;
                }

                channelContext.Send(groupInfo, CommandRetention.GROUPS_REMOVE_FLAG);
            }
        }

        private void RemoveGroupInfo(ActorChannelContext context)
        {
            var channelId = context.ChannelId;
            if (!ServerGroupInfoChannelMapping.TryRemove(channelId, out ConcurrentDictionary<string, GroupInfo> channelGroupInfos))
                return;

            if (ActorInfoChannelMapping.TryRemove(context.ChannelId, out ConcurrentDictionary<string, ActorLocationInfo> channelActorInfos))
            {
                var actorInfos = channelActorInfos.Values;
                foreach (var actorInfo in actorInfos)
                    Remove(actorInfo);
            }

            foreach (var kv in channelGroupInfos)
            {
                if (!ServerGroupInfoMapping.TryGetValue(kv.Key, out ConcurrentDictionary<int, GroupInfo> groupChannelInfos))
                    continue;

                if (groupChannelInfos.TryRemove(channelId, out GroupInfo value))
                    BroadcastRemoveGroupInfo(context, value);
            }
        }

        /// <summary>
        /// 新增Actor位置信息
        /// </summary>
        /// <param name="actorInfo"></param>
        public void AddActorLocationInfo(ActorLocationInfo actorInfo)
        {
            if (!ServerGroupInfoMapping.ContainsKey(actorInfo.ServerGroup))
            {
                actorInfo.Stated = ActorLocationState.GroupLocationNotExisted;
                return;
            }

            var hostKey = KeyGenerator.CreateHostKey(actorInfo);
            var actorKey = KeyGenerator.CreateActorKey(actorInfo);
            var actorTypeKey = KeyGenerator.CreateActorTypeKey(actorInfo);
            var serverGroupKey = KeyGenerator.CreateServerGroupKey(actorInfo.ServerGroup, actorInfo.IpAddress, actorInfo.Port);

            if (!ChannelContextHostMapping.TryGetValue(hostKey, out ActorChannelContext context))
            {
                actorInfo.Stated = ActorLocationState.GroupServerNotExisted;
                return;
            }

            actorInfo.ChannelId = context.ChannelId;
            if (ActorInfoMapping.ContainsKey(actorKey))
            {
                actorInfo.Stated = ActorLocationState.Successful;
                return;
            }

            ActorInfoMapping.AddOrUpdate(actorKey, actorInfo, (k, v) => actorInfo);

            //Actor类型Actor信息缓存
            if (!ActorInfoTypeMapping.TryGetValue(actorTypeKey, out ConcurrentDictionary<string, ActorLocationInfo> typeActorInfos))
            {
                typeActorInfos = new ConcurrentDictionary<string, ActorLocationInfo>();
                ActorInfoTypeMapping.AddOrUpdate(actorTypeKey, typeActorInfos, (k, v) => typeActorInfos);
            }
            typeActorInfos.AddOrUpdate(actorKey, actorInfo, (k, v) => actorInfo);

            //主机地址Actor信息缓存
            if (!ActorInfoHostMapping.TryGetValue(hostKey, out ConcurrentDictionary<string, ActorLocationInfo> hostActorInfos))
            {
                hostActorInfos = new ConcurrentDictionary<string, ActorLocationInfo>();
                ActorInfoHostMapping.AddOrUpdate(hostKey, hostActorInfos, (k, v) => hostActorInfos);
            }
            hostActorInfos.AddOrUpdate(actorKey, actorInfo, (k, v) => actorInfo);

            //管道连接Actor信息缓存
            if (!ActorInfoChannelMapping.TryGetValue(context.ChannelId, out ConcurrentDictionary<string, ActorLocationInfo> channelActorInfos))
            {
                channelActorInfos = new ConcurrentDictionary<string, ActorLocationInfo>();
                ActorInfoChannelMapping.AddOrUpdate(context.ChannelId, channelActorInfos, (k, v) => channelActorInfos);
            }
            channelActorInfos.AddOrUpdate(actorKey, actorInfo, (k, v) => actorInfo);

            //服务分组Actor信息缓存
            if (!ActorInfoServerGroupMapping.TryGetValue(serverGroupKey, out ConcurrentDictionary<string, ActorLocationInfo> groupActorInfos))
            {
                groupActorInfos = new ConcurrentDictionary<string, ActorLocationInfo>();
                ActorInfoServerGroupMapping.AddOrUpdate(serverGroupKey, groupActorInfos, (k, v) => groupActorInfos);
            }
            groupActorInfos.AddOrUpdate(actorKey, actorInfo, (k, v) => actorInfo);
            actorInfo.Stated = ActorLocationState.Successful;
        }

        /// <summary>
        /// 获取Actor位置信息
        /// </summary>
        /// <param name="serverGroup"></param>
        /// <param name="actorType"></param>
        /// <param name="actorId"></param>
        /// <returns></returns>
        public ActorLocationInfo GetActorLocationInfo(string serverGroup, string actorType, string actorId)
        {
            var actorKey = KeyGenerator.CreateActorKey(serverGroup, actorType, actorId);
            if (!ActorInfoMapping.TryGetValue(actorKey, out ActorLocationInfo actorLocationInfo))
                return null;

            return actorLocationInfo;
        }

        /// <summary>
        /// 获取一个Actor类型所有Actor位置信息列表
        /// </summary>
        /// <param name="serverGroup"></param>
        /// <param name="actorType"></param>
        /// <returns></returns>
        public List<ActorLocationInfo> GetActorTypeLocationList(string serverGroup, string actorType)
        {
            var actorTypeKey = KeyGenerator.CreateActorTypeKey(serverGroup, actorType);
            if (!ActorInfoTypeMapping.TryGetValue(actorTypeKey, out ConcurrentDictionary<string, ActorLocationInfo> typeActorInfos))
                return null;

            return typeActorInfos.Values.ToList();
        }

        /// <summary>
        /// 删除Actor位置信息
        /// </summary>
        /// <param name="actorInfo">Actor位置信息</param>
        /// <param name="broadcast">是否广播给其他的集群服务</param>
        public void Remove(ActorLocationInfo actorInfo, bool broadcast = false)
        {
            var hostKey = KeyGenerator.CreateHostKey(actorInfo);
            var actorKey = KeyGenerator.CreateActorKey(actorInfo);
            var actorTypeKey = KeyGenerator.CreateActorTypeKey(actorInfo);
            var serverGroupKey = KeyGenerator.CreateServerGroupKey(actorInfo.ServerGroup, actorInfo.IpAddress, actorInfo.Port);

            if (!ChannelContextHostMapping.TryGetValue(hostKey, out ActorChannelContext context))
                return;

            ActorInfoMapping.TryRemove(actorKey, out _);

            if (ActorInfoTypeMapping.TryGetValue(actorTypeKey, out ConcurrentDictionary<string, ActorLocationInfo> typeActorInfos))
                typeActorInfos.TryRemove(actorKey, out _);

            if (ActorInfoHostMapping.TryGetValue(hostKey, out ConcurrentDictionary<string, ActorLocationInfo> hostActorInfos))
                hostActorInfos.TryRemove(actorKey, out _);

            if (ActorInfoServerGroupMapping.TryGetValue(serverGroupKey, out ConcurrentDictionary<string, ActorLocationInfo> groupActorInfos))
                groupActorInfos.TryRemove(actorKey, out _);

            //广播Actor取消注册消息
            if (broadcast)
                BroadcastUnRegisterActorLocationInfo(context, actorInfo);
        }

        private void BroadcastUnRegisterActorLocationInfo(ActorChannelContext context, ActorLocationInfo actorInfo)
        {
            var channelContexts = ServerNetwork.GetContextList();
            if (!channelContexts.Any())
                return;

            var actorKey = KeyGenerator.CreateActorKey(actorInfo);
            foreach (var channelContext in channelContexts)
            {
                if (ActorInfoChannelMapping.TryGetValue(channelContext.ChannelId, out ConcurrentDictionary<string, ActorLocationInfo> channelActorInfos))
                    channelActorInfos.TryRemove(actorKey, out _);

                if (context.ChannelId == channelContext.ChannelId)
                    continue;

                try
                {
                    channelContext.Send(actorInfo, CommandRetention.ACTOR_REMOVE_FLAG);
                }
                catch (Exception ex)
                {
                    ExceptionHandler?.Invoke(channelContext, ex);
                }
            }
        }
    }
}
